/*********************************************************************/
/* Copyright 2009, 2010 The University of Texas at Austin.           */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. Redistributions in binary form must reproduce the above      */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer in the documentation and/or other materials       */
/*      provided with the distribution.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``AS IS''  AND ANY  EXPRESS OR  IMPLIED WARRANTIES,    */
/*    INCLUDING, BUT  NOT LIMITED  TO, THE IMPLIED  WARRANTIES OF    */
/*    MERCHANTABILITY  AND FITNESS FOR  A PARTICULAR  PURPOSE ARE    */
/*    DISCLAIMED.  IN  NO EVENT SHALL THE UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN OR CONTRIBUTORS BE  LIABLE FOR ANY DIRECT, INDIRECT,    */
/*    INCIDENTAL,  SPECIAL, EXEMPLARY,  OR  CONSEQUENTIAL DAMAGES    */
/*    (INCLUDING, BUT  NOT LIMITED TO,  PROCUREMENT OF SUBSTITUTE    */
/*    GOODS  OR  SERVICES; LOSS  OF  USE,  DATA,  OR PROFITS;  OR    */
/*    BUSINESS INTERRUPTION) HOWEVER CAUSED  AND ON ANY THEORY OF    */
/*    LIABILITY, WHETHER  IN CONTRACT, STRICT  LIABILITY, OR TORT    */
/*    (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY OUT    */
/*    OF  THE  USE OF  THIS  SOFTWARE,  EVEN  IF ADVISED  OF  THE    */
/*    POSSIBILITY OF SUCH DAMAGE.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#include <stdio.h>
#include <string.h>
#include "cpuid.h"

#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#endif

#if defined(_MSC_VER) && !defined(__clang__)
#define C_INLINE __inline
#else
#define C_INLINE inline
#endif

/*
#ifdef NO_AVX
#define CPUTYPE_HASWELL CPUTYPE_NEHALEM
#define CORE_HASWELL CORE_NEHALEM
#define CPUTYPE_SKYLAKEX CPUTYPE_NEHALEM
#define CORE_SKYLAKEX CORE_NEHALEM
#define CPUTYPE_SANDYBRIDGE CPUTYPE_NEHALEM
#define CORE_SANDYBRIDGE CORE_NEHALEM
#define CPUTYPE_BULLDOZER CPUTYPE_BARCELONA
#define CORE_BULLDOZER CORE_BARCELONA
#define CPUTYPE_PILEDRIVER CPUTYPE_BARCELONA
#define CORE_PILEDRIVER CORE_BARCELONA
#endif
*/

// defined(__x86_64__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64)

#if defined(__x86_64__) || defined(_M_X64)
#if ((defined(__GNUC__)  && __GNUC__ > 6 && defined(__AVX2__)) || (defined(__clang__) && __clang_major__ >= 6))
// Equivalent to define HAVE_AVX512
#else
#ifndef NO_AVX512
#define NO_AVX512
#endif
#endif
#endif // __x86_64__

//
// x86_64 cpuid : https://www.cnblogs.com/TaigaCon/p/7882216.html
//

#if defined(_MSC_VER) && !defined(__clang__)

void cpuid(int op, int * eax, int * ebx, int * ecx, int * edx)
{
    int cpuInfo[4] = { -1 };

    __cpuid(cpuInfo, op);

    *eax = cpuInfo[0];
    *ebx = cpuInfo[1];
    *ecx = cpuInfo[2];
    *edx = cpuInfo[3];
}

void cpuid_count(int op, int count, int * eax, int * ebx, int * ecx, int * edx)
{
    int cpuInfo[4] = { -1 };

    __cpuidex(cpuInfo, op, count);

    *eax = cpuInfo[0];
    *ebx = cpuInfo[1];
    *ecx = cpuInfo[2];
    *edx = cpuInfo[3];
}

#else

#ifndef CPUIDEMU

#if defined(__APPLE__) && defined(__i386__)

void cpuid(int op, int * eax, int * ebx, int * ecx, int * edx);
void cpuid_count(int op, int count, int * eax, int * ebx, int * ecx, int * edx);

#else // !(__APPLE__ && __i386__)

static C_INLINE void cpuid(int op, int * eax, int * ebx, int * ecx, int * edx)
{
#if defined(__i386__) && defined(__PIC__)
    __asm__ __volatile__
    ("mov %%ebx, %%edi;"
     "cpuid;"
     "xchgl %%ebx, %%edi;"
     : "=a" (*eax), "=D" (*ebx), "=c" (*ecx), "=d" (*edx) : "a" (op), "c" (0) : "cc");
#else
    __asm__ __volatile__
    ("cpuid": "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "a" (op), "c" (0) : "cc");
#endif
}

static C_INLINE void cpuid_count(int op, int count, int * eax, int * ebx, int * ecx, int * edx)
{
#if defined(__i386__) && defined(__PIC__)
    __asm__ __volatile__
    ("mov %%ebx, %%edi;"
     "cpuid;"
     "xchgl %%ebx, %%edi;"
     : "=a" (*eax), "=D" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (op), "2" (count) : "cc");
#else
    __asm__ __volatile__
    ("cpuid": "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (op), "2" (count) : "cc");
#endif
}
#endif // (__APPLE__ && __i386__)

#else // defined(CPUIDEMU)

typedef struct {
    unsigned int id, a, b, c, d;
} idlist_t;

typedef struct {
    char * vendor;
    char * name;
    int start, stop;
} vendor_t;

extern idlist_t idlist [];
extern vendor_t vendor [];

static int cv = VENDOR;

void cpuid(unsigned int op, unsigned int * eax, unsigned int * ebx,
           unsigned int * ecx, unsigned int * edx)
{
    static int current = 0;

    int start = vendor[cv].start;
    int stop = vendor[cv].stop;
    int count = stop - start;

    if ((current < start) || (current > stop)) current = start;

    while ((count > 0) && (idlist[current].id != op)) {
        current++;
        if (current > stop) current = start;
        count--;
    }

    *eax = idlist[current].a;
    *ebx = idlist[current].b;
    *ecx = idlist[current].c;
    *edx = idlist[current].d;
}

void cpuid_count(unsigned int op, unsigned int count, unsigned int * eax,
                 unsigned int * ebx, unsigned int * ecx, unsigned int * edx) {
    return cpuid(op, eax, ebx, ecx, edx);
}

#endif // !defined(CPUIDEMU)

#endif // (_MSC_VER && !__clang__)

static C_INLINE int have_cpuid(void)
{
    int eax, ebx, ecx, edx;

    cpuid(0, &eax, &ebx, &ecx, &edx);

    return eax;
}

static C_INLINE int have_excpuid(void)
{
    int eax, ebx, ecx, edx;

    cpuid(0x80000000, &eax, &ebx, &ecx, &edx);

    return eax & 0xffff;
}

#ifndef NO_AVX
struct reg64_t {
    int low;
    int high;
};

static C_INLINE void xgetbv(int op, int * eax, int * edx) {
    // Use binary code for xgetbv
#if defined(_MSC_VER) && !defined(__clang__)
    unsigned __int64 result = __xgetbv(op);

    struct reg64_t * r64 = (struct reg64_t *)&result;
    *eax = r64->low;
    *edx = r64->high;
#else
    __asm__ __volatile__
    (".byte 0x0f, 0x01, 0xd0": "=a" (*eax), "=d" (*edx) : "c" (op) : "cc");
#endif // _MSC_VER
}
#endif // NO_AVX

int support_avx()
{
#ifndef NO_AVX
    int eax, ebx, ecx, edx;
    int ret = 0;

    cpuid(1, &eax, &ebx, &ecx, &edx);

    if ((ecx & (1 << 28)) != 0 && (ecx & (1 << 27)) != 0 && (ecx & (1 << 26)) != 0) {
        xgetbv(0, &eax, &edx);
        if ((eax & 6) == 6) {
            ret = 1;  // CPU support AVX
        }
    }
    return ret;
#else
    return 0;
#endif // NO_AVX
}

int support_avx2() {
#ifndef NO_AVX2
    int eax, ebx, ecx = 0, edx;
    int ret = 0;

    if (!support_avx())
        return 0;

    cpuid(7, &eax, &ebx, &ecx, &edx);

    if ((ebx & (1 << 5)) != 0)
        ret = 1;  // CPU have AVX2 flag
    return ret;
#else
    return 0;
#endif // NO_AVX2
}

int support_avx512()
{
#if !defined(NO_AVX) && !defined(NO_AVX512)
    int eax, ebx, ecx, edx;
    int ret = 0;

    if (!support_avx())
        return 0;

    cpuid(7, &eax, &ebx, &ecx, &edx);

    if ((ebx & (1 << 5)) == 0) {
        ret = 0;  // cpu does not have avx2 flag
    }
    if ((ebx & (1 << 31)) != 0) { // CPU have AVX512VL flag
        xgetbv(0, &eax, &edx);
        if ((eax & 0xe0) == 0xe0)
            ret = 1;  // CPU supports saving zmm registers
    }
    return ret;
#else
    return 0;
#endif // NO_AVX512
}

int support_avx512_bf16()
{
#if !defined(NO_AVX) && !defined(NO_AVX512)
    int eax, ebx, ecx, edx;
    int ret = 0;

    if (!support_avx512())
        return 0;

    cpuid_count(7, 1, &eax, &ebx, &ecx, &edx);

    if ((eax & (1 << 5)) != 0) {
        ret = 1;  // CPUID.7.1:EAX[bit 5] indicates whether avx512_bf16 supported or not
    }
    return ret;
#else
    return 0;
#endif // NO_AVX512
}

#define BIT_AMX_TILE    0x01000000
#define BIT_AMX_BF16    0x00400000
#define BIT_AMX_ENBD    0x00060000

int support_amx_bf16()
{
#if !defined(NO_AVX) && !defined(NO_AVX512)
    int eax, ebx, ecx, edx;
    int ret = 0;

    if (!support_avx512())
        return 0;

    // CPUID.7.0:EDX indicates AMX support
    cpuid_count(7, 0, &eax, &ebx, &ecx, &edx);

    if ((edx & BIT_AMX_TILE) && (edx & BIT_AMX_BF16)) {
        // CPUID.D.0:EAX[17:18] indicates AMX enabled
        cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
        if ((eax & BIT_AMX_ENBD) == BIT_AMX_ENBD)
            ret = 1;
    }
    return ret;
#else
    return 0;
#endif
}

int get_vendor(void)
{
    int eax, ebx, ecx, edx;
    char vendor[13];

    cpuid(0, &eax, &ebx, &ecx, &edx);

    *(int * )(&vendor[0]) = ebx;
    *(int * )(&vendor[4]) = edx;
    *(int * )(&vendor[8]) = ecx;
    vendor[12] = (char)0;

    if (!strcmp(vendor, "GenuineIntel")) return VENDOR_INTEL;
    if (!strcmp(vendor, " UMC UMC UMC")) return VENDOR_UMC;
    if (!strcmp(vendor, "AuthenticAMD")) return VENDOR_AMD;
    if (!strcmp(vendor, "CyrixInstead")) return VENDOR_CYRIX;
    if (!strcmp(vendor, "NexGenDriven")) return VENDOR_NEXGEN;
    if (!strcmp(vendor, "CentaurHauls")) return VENDOR_CENTAUR;
    if (!strcmp(vendor, "  Shanghai  ")) return VENDOR_ZHAOXIN;
    if (!strcmp(vendor, "RiseRiseRise")) return VENDOR_RISE;
    if (!strcmp(vendor, " SiS SiS SiS")) return VENDOR_SIS;
    if (!strcmp(vendor, "GenuineTMx86")) return VENDOR_TRANSMETA;
    if (!strcmp(vendor, "Geode by NSC")) return VENDOR_NSC;
    if (!strcmp(vendor, "HygonGenuine")) return VENDOR_HYGON;

    if ((eax == 0) || ((eax & 0x500) != 0)) return VENDOR_INTEL;

    return VENDOR_UNKNOWN;
}

int get_cpu_info(int info_type)
{
    int eax, ebx, ecx, edx;
    // int extend_family, family;
    // int extend_model, model;
    // int cpu_type, stepping;
    int feature = 0;

    cpuid(1, &eax, &ebx, &ecx, &edx);

    switch (info_type) {
        case GET_EXFAMILY:
            return BITMASK(eax, 20, 0xff);
        case GET_EXMODEL:
            return BITMASK(eax, 16, 0x0f);
        case GET_TYPE:
            return BITMASK(eax, 12, 0x03);
        case GET_FAMILY:
            return BITMASK(eax, 8, 0x0f);
        case GET_MODEL:
            return BITMASK(eax, 4, 0x0f);
        case GET_APICID:
            return BITMASK(ebx, 24, 0x0f);
        case GET_LCOUNT:
            return BITMASK(ebx, 16, 0x0f);
        case GET_CHUNKS:
            return BITMASK(ebx, 8, 0x0f);
        case GET_STEPPING:
            return BITMASK(eax, 0, 0x0f);
        case GET_BLANDID:
            return BITMASK(ebx, 0, 0xff);
        case GET_NUMSHARE:
            if (have_cpuid() < 4) return 0;
            cpuid(4, &eax, &ebx, &ecx, &edx);
            return BITMASK(eax, 14, 0xfff);
        case GET_NUMCORES:
            if (have_cpuid() < 4) return 0;
            cpuid(4, &eax, &ebx, &ecx, &edx);
            return BITMASK(eax, 26, 0x3f);

        case GET_FEATURE:
            if ((edx & (1 << 3)) != 0) feature |= HAVE_PSE;
            if ((edx & (1 << 15)) != 0) feature |= HAVE_CMOV;
            if ((edx & (1 << 19)) != 0) feature |= HAVE_CFLUSH;
            if ((edx & (1 << 23)) != 0) feature |= HAVE_MMX;
            if ((edx & (1 << 25)) != 0) feature |= HAVE_SSE;
            if ((edx & (1 << 26)) != 0) feature |= HAVE_SSE2;
            if ((edx & (1 << 27)) != 0) {
                if (BITMASK(ebx, 16, 0x0f) > 0) feature |= HAVE_HIT;
            }
            if ((ecx & (1 << 0)) != 0) feature |= HAVE_SSE3;
            if ((ecx & (1 << 9)) != 0) feature |= HAVE_SSSE3;
            if ((ecx & (1 << 19)) != 0) feature |= HAVE_SSE4_1;
            if ((ecx & (1 << 20)) != 0) feature |= HAVE_SSE4_2;
#ifndef NO_AVX
            if (support_avx()) feature |= HAVE_AVX;
            if (support_avx2()) feature |= HAVE_AVX2;
            if (support_avx512()) feature |= HAVE_AVX512VL;
            if (support_avx512_bf16()) feature |= HAVE_AVX512BF16;
            if (support_amx_bf16()) feature |= HAVE_AMXBF16;
            if ((ecx & (1 << 12)) != 0) feature |= HAVE_FMA3;
#endif
            if (have_excpuid() >= 0x01) {
                cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
                if ((ecx & (1 << 6)) != 0) feature |= HAVE_SSE4A;
                if ((ecx & (1 << 7)) != 0) feature |= HAVE_MISALIGNSSE;
#ifndef NO_AVX
                if ((ecx & (1 << 16)) != 0) feature |= HAVE_FMA4;
#endif
                if ((edx & (1 << 30)) != 0) feature |= HAVE_3DNOWEX;
                if ((edx & (1 << 31)) != 0) feature |= HAVE_3DNOW;
            }

            if (have_excpuid() >= 0x1a) {
                cpuid(0x8000001a, &eax, &ebx, &ecx, &edx);
                if ((eax & (1 << 0)) != 0) feature |= HAVE_128BITFPU;
                if ((eax & (1 << 1)) != 0) feature |= HAVE_FASTMOVU;
            }
            break;
    }

    return feature;
}

int get_cacheinfo(int type, cache_info_t * cacheinfo)
{
    int eax, ebx, ecx, edx, cpuid_level;
    int info[15];
    int i;
    cache_info_t LC1, LD1, L2, L3,
                 ITB, DTB, LITB, LDTB,
                 L2ITB, L2DTB, L2LITB, L2LDTB;

    LC1.size    = 0; LC1.associative    = 0; LC1.linesize    = 0; LC1.shared    = 0;
    LD1.size    = 0; LD1.associative    = 0; LD1.linesize    = 0; LD1.shared    = 0;
    L2.size     = 0; L2.associative     = 0; L2.linesize     = 0; L2.shared     = 0;
    L3.size     = 0; L3.associative     = 0; L3.linesize     = 0; L3.shared     = 0;
    ITB.size    = 0; ITB.associative    = 0; ITB.linesize    = 0; ITB.shared    = 0;
    DTB.size    = 0; DTB.associative    = 0; DTB.linesize    = 0; DTB.shared    = 0;
    LITB.size   = 0; LITB.associative   = 0; LITB.linesize   = 0; LITB.shared   = 0;
    LDTB.size   = 0; LDTB.associative   = 0; LDTB.linesize   = 0; LDTB.shared   = 0;
    L2ITB.size  = 0; L2ITB.associative  = 0; L2ITB.linesize  = 0; L2ITB.shared  = 0;
    L2DTB.size  = 0; L2DTB.associative  = 0; L2DTB.linesize  = 0; L2DTB.shared  = 0;
    L2LITB.size = 0; L2LITB.associative = 0; L2LITB.linesize = 0; L2LITB.shared = 0;
    L2LDTB.size = 0; L2LDTB.associative = 0; L2LDTB.linesize = 0; L2LDTB.shared = 0;

    cpuid(0, &cpuid_level, &ebx, &ecx, &edx);

    if (cpuid_level > 1) {
        int numcalls = 0;

        cpuid(2, &eax, &ebx, &ecx, &edx);

        numcalls = BITMASK(eax, 0, 0xff); // FIXME some systems may require repeated calls to read all entries
        info[0] = BITMASK(eax, 8, 0xff);
        info[1] = BITMASK(eax, 16, 0xff);
        info[2] = BITMASK(eax, 24, 0xff);

        info[3] = BITMASK(ebx, 0, 0xff);
        info[4] = BITMASK(ebx, 8, 0xff);
        info[5] = BITMASK(ebx, 16, 0xff);
        info[6] = BITMASK(ebx, 24, 0xff);

        info[7] = BITMASK(ecx, 0, 0xff);
        info[8] = BITMASK(ecx, 8, 0xff);
        info[9] = BITMASK(ecx, 16, 0xff);
        info[10] = BITMASK(ecx, 24, 0xff);

        info[11] = BITMASK(edx, 0, 0xff);
        info[12] = BITMASK(edx, 8, 0xff);
        info[13] = BITMASK(edx, 16, 0xff);
        info[14] = BITMASK(edx, 24, 0xff);

        for (i = 0; i < 15; i++) {
            switch (info[i]) {
                /* This table is from http://www.sandpile.org/ia32/cpuid.htm */
                case 0x01:
                    ITB.size = 4;
                    ITB.associative = 4;
                    ITB.linesize = 32;
                    break;
                case 0x02:
                    LITB.size = 4096;
                    LITB.associative = 0;
                    LITB.linesize = 2;
                    break;
                case 0x03:
                    DTB.size = 4;
                    DTB.associative = 4;
                    DTB.linesize = 64;
                    break;
                case 0x04:
                    LDTB.size = 4096;
                    LDTB.associative = 4;
                    LDTB.linesize = 8;
                    break;
                case 0x05:
                    LDTB.size = 4096;
                    LDTB.associative = 4;
                    LDTB.linesize = 32;
                    break;
                case 0x06:
                    LC1.size = 8;
                    LC1.associative = 4;
                    LC1.linesize = 32;
                    break;
                case 0x08:
                    LC1.size = 16;
                    LC1.associative = 4;
                    LC1.linesize = 32;
                    break;
                case 0x09:
                    LC1.size = 32;
                    LC1.associative = 4;
                    LC1.linesize = 64;
                    break;
                case 0x0a:
                    LD1.size = 8;
                    LD1.associative = 2;
                    LD1.linesize = 32;
                    break;
                case 0x0c:
                    LD1.size = 16;
                    LD1.associative = 4;
                    LD1.linesize = 32;
                    break;
                case 0x0d:
                    LD1.size = 16;
                    LD1.associative = 4;
                    LD1.linesize = 64;
                    break;
                case 0x0e:
                    LD1.size = 24;
                    LD1.associative = 6;
                    LD1.linesize = 64;
                    break;
                case 0x10:
                    LD1.size = 16;
                    LD1.associative = 4;
                    LD1.linesize = 32;
                    break;
                case 0x15:
                    LC1.size = 16;
                    LC1.associative = 4;
                    LC1.linesize = 32;
                    break;
                case 0x1a:
                    L2.size = 96;
                    L2.associative = 6;
                    L2.linesize = 64;
                    break;
                case 0x21:
                    L2.size = 256;
                    L2.associative = 8;
                    L2.linesize = 64;
                    break;
                case 0x22:
                    L3.size = 512;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0x23:
                    L3.size = 1024;
                    L3.associative = 8;
                    L3.linesize = 64;
                    break;
                case 0x25:
                    L3.size = 2048;
                    L3.associative = 8;
                    L3.linesize = 64;
                    break;
                case 0x29:
                    L3.size = 4096;
                    L3.associative = 8;
                    L3.linesize = 64;
                    break;
                case 0x2c:
                    LD1.size = 32;
                    LD1.associative = 8;
                    LD1.linesize = 64;
                    break;
                case 0x30:
                    LC1.size = 32;
                    LC1.associative = 8;
                    LC1.linesize = 64;
                    break;
                case 0x39:
                    L2.size = 128;
                    L2.associative = 4;
                    L2.linesize = 64;
                    break;
                case 0x3a:
                    L2.size = 192;
                    L2.associative = 6;
                    L2.linesize = 64;
                    break;
                case 0x3b:
                    L2.size = 128;
                    L2.associative = 2;
                    L2.linesize = 64;
                    break;
                case 0x3c:
                    L2.size = 256;
                    L2.associative = 4;
                    L2.linesize = 64;
                    break;
                case 0x3d:
                    L2.size = 384;
                    L2.associative = 6;
                    L2.linesize = 64;
                    break;
                case 0x3e:
                    L2.size = 512;
                    L2.associative = 4;
                    L2.linesize = 64;
                    break;
                case 0x41:
                    L2.size = 128;
                    L2.associative = 4;
                    L2.linesize = 32;
                    break;
                case 0x42:
                    L2.size = 256;
                    L2.associative = 4;
                    L2.linesize = 32;
                    break;
                case 0x43:
                    L2.size = 512;
                    L2.associative = 4;
                    L2.linesize = 32;
                    break;
                case 0x44:
                    L2.size = 1024;
                    L2.associative = 4;
                    L2.linesize = 32;
                    break;
                case 0x45:
                    L2.size = 2048;
                    L2.associative = 4;
                    L2.linesize = 32;
                    break;
                case 0x46:
                    L3.size = 4096;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0x47:
                    L3.size = 8192;
                    L3.associative = 8;
                    L3.linesize = 64;
                    break;
                case 0x48:
                    L2.size = 3184;
                    L2.associative = 12;
                    L2.linesize = 64;
                    break;
                case 0x49:
                    if ((get_cpu_info(GET_FAMILY) == 0x0f) && (get_cpu_info(GET_MODEL) == 0x06)) {
                        L3.size = 4096;
                        L3.associative = 16;
                        L3.linesize = 64;
                    }
                    else {
                        L2.size = 4096;
                        L2.associative = 16;
                        L2.linesize = 64;
                    }
                    break;
                case 0x4a:
                    L3.size = 6144;
                    L3.associative = 12;
                    L3.linesize = 64;
                    break;
                case 0x4b:
                    L3.size = 8192;
                    L3.associative = 16;
                    L3.linesize = 64;
                    break;
                case 0x4c:
                    L3.size = 12280;
                    L3.associative = 12;
                    L3.linesize = 64;
                    break;
                case 0x4d:
                    L3.size = 16384;
                    L3.associative = 16;
                    L3.linesize = 64;
                    break;
                case 0x4e:
                    L2.size = 6144;
                    L2.associative = 24;
                    L2.linesize = 64;
                    break;
                case 0x4f:
                    ITB.size = 4;
                    ITB.associative = 0;
                    ITB.linesize = 32;
                    break;
                case 0x50:
                    ITB.size = 4;
                    ITB.associative = 0;
                    ITB.linesize = 64;
                    LITB.size = 4096;
                    LITB.associative = 0;
                    LITB.linesize = 64;
                    LITB.shared = 1;
                    break;
                case 0x51:
                    ITB.size = 4;
                    ITB.associative = 0;
                    ITB.linesize = 128;
                    LITB.size = 4096;
                    LITB.associative = 0;
                    LITB.linesize = 128;
                    LITB.shared = 1;
                    break;
                case 0x52:
                    ITB.size = 4;
                    ITB.associative = 0;
                    ITB.linesize = 256;
                    LITB.size = 4096;
                    LITB.associative = 0;
                    LITB.linesize = 256;
                    LITB.shared = 1;
                    break;
                case 0x55:
                    LITB.size = 4096;
                    LITB.associative = 0;
                    LITB.linesize = 7;
                    LITB.shared = 1;
                    break;
                case 0x56:
                    LDTB.size = 4096;
                    LDTB.associative = 4;
                    LDTB.linesize = 16;
                    break;
                case 0x57:
                    LDTB.size = 4096;
                    LDTB.associative = 4;
                    LDTB.linesize = 16;
                    break;
                case 0x5b:
                    DTB.size = 4;
                    DTB.associative = 0;
                    DTB.linesize = 64;
                    LDTB.size = 4096;
                    LDTB.associative = 0;
                    LDTB.linesize = 64;
                    LDTB.shared = 1;
                    break;
                case 0x5c:
                    DTB.size = 4;
                    DTB.associative = 0;
                    DTB.linesize = 128;
                    LDTB.size = 4096;
                    LDTB.associative = 0;
                    LDTB.linesize = 128;
                    LDTB.shared = 1;
                    break;
                case 0x5d:
                    DTB.size = 4;
                    DTB.associative = 0;
                    DTB.linesize = 256;
                    LDTB.size = 4096;
                    LDTB.associative = 0;
                    LDTB.linesize = 256;
                    LDTB.shared = 1;
                    break;
                case 0x60:
                    LD1.size = 16;
                    LD1.associative = 8;
                    LD1.linesize = 64;
                    break;
                case 0x63:
                    DTB.size = 2048;
                    DTB.associative = 4;
                    DTB.linesize = 32;
                    LDTB.size = 4096;
                    LDTB.associative = 4;
                    LDTB.linesize = 32;
                    break;
                case 0x66:
                    LD1.size = 8;
                    LD1.associative = 4;
                    LD1.linesize = 64;
                    break;
                case 0x67:
                    LD1.size = 16;
                    LD1.associative = 4;
                    LD1.linesize = 64;
                    break;
                case 0x68:
                    LD1.size = 32;
                    LD1.associative = 4;
                    LD1.linesize = 64;
                    break;
                case 0x70:
                    LC1.size = 12;
                    LC1.associative = 8;
                    break;
                case 0x71:
                    LC1.size = 16;
                    LC1.associative = 8;
                    break;
                case 0x72:
                    LC1.size = 32;
                    LC1.associative = 8;
                    break;
                case 0x73:
                    LC1.size = 64;
                    LC1.associative = 8;
                    break;
                case 0x76:
                    ITB.size = 2048;
                    ITB.associative = 0;
                    ITB.linesize = 8;
                    LITB.size = 4096;
                    LITB.associative = 0;
                    LITB.linesize = 8;
                    break;
                case 0x77:
                    LC1.size = 16;
                    LC1.associative = 4;
                    LC1.linesize = 64;
                    break;
                case 0x78:
                    L2.size = 1024;
                    L2.associative = 4;
                    L2.linesize = 64;
                    break;
                case 0x79:
                    L2.size = 128;
                    L2.associative = 8;
                    L2.linesize = 64;
                    break;
                case 0x7a:
                    L2.size = 256;
                    L2.associative = 8;
                    L2.linesize = 64;
                    break;
                case 0x7b:
                    L2.size = 512;
                    L2.associative = 8;
                    L2.linesize = 64;
                    break;
                case 0x7c:
                    L2.size = 1024;
                    L2.associative = 8;
                    L2.linesize = 64;
                    break;
                case 0x7d:
                    L2.size = 2048;
                    L2.associative = 8;
                    L2.linesize = 64;
                    break;
                case 0x7e:
                    L2.size = 256;
                    L2.associative = 8;
                    L2.linesize = 128;
                    break;
                case 0x7f:
                    L2.size = 512;
                    L2.associative = 2;
                    L2.linesize = 64;
                    break;
                case 0x81:
                    L2.size = 128;
                    L2.associative = 8;
                    L2.linesize = 32;
                    break;
                case 0x82:
                    L2.size = 256;
                    L2.associative = 8;
                    L2.linesize = 32;
                    break;
                case 0x83:
                    L2.size = 512;
                    L2.associative = 8;
                    L2.linesize = 32;
                    break;
                case 0x84:
                    L2.size = 1024;
                    L2.associative = 8;
                    L2.linesize = 32;
                    break;
                case 0x85:
                    L2.size = 2048;
                    L2.associative = 8;
                    L2.linesize = 32;
                    break;
                case 0x86:
                    L2.size = 512;
                    L2.associative = 4;
                    L2.linesize = 64;
                    break;
                case 0x87:
                    L2.size = 1024;
                    L2.associative = 8;
                    L2.linesize = 64;
                    break;
                case 0x88:
                    L3.size = 2048;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0x89:
                    L3.size = 4096;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0x8a:
                    L3.size = 8192;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0x8d:
                    L3.size = 3096;
                    L3.associative = 12;
                    L3.linesize = 128;
                    break;
                case 0x90:
                    ITB.size = 4;
                    ITB.associative = 0;
                    ITB.linesize = 64;
                    break;
                case 0x96:
                    DTB.size = 4;
                    DTB.associative = 0;
                    DTB.linesize = 32;
                    break;
                case 0x9b:
                    L2DTB.size = 4;
                    L2DTB.associative = 0;
                    L2DTB.linesize = 96;
                    break;
                case 0xb0:
                    ITB.size = 4;
                    ITB.associative = 4;
                    ITB.linesize = 128;
                    break;
                case 0xb1:
                    LITB.size = 4096;
                    LITB.associative = 4;
                    LITB.linesize = 4;
                    break;
                case 0xb2:
                    ITB.size = 4;
                    ITB.associative = 4;
                    ITB.linesize = 64;
                    break;
                case 0xb3:
                    DTB.size = 4;
                    DTB.associative = 4;
                    DTB.linesize = 128;
                    break;
                case 0xb4:
                    DTB.size = 4;
                    DTB.associative = 4;
                    DTB.linesize = 256;
                    break;
                case 0xba:
                    DTB.size = 4;
                    DTB.associative = 4;
                    DTB.linesize = 64;
                    break;
                case 0xd0:
                    L3.size = 512;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0xd1:
                    L3.size = 1024;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0xd2:
                    L3.size = 2048;
                    L3.associative = 4;
                    L3.linesize = 64;
                    break;
                case 0xd6:
                    L3.size = 1024;
                    L3.associative = 8;
                    L3.linesize = 64;
                    break;
                case 0xd7:
                    L3.size = 2048;
                    L3.associative = 8;
                    L3.linesize = 64;
                    break;
                case 0xd8:
                    L3.size = 4096;
                    L3.associative = 8;
                    L3.linesize = 64;
                    break;
                case 0xdc:
                    L3.size = 2048;
                    L3.associative = 12;
                    L3.linesize = 64;
                    break;
                case 0xdd:
                    L3.size = 4096;
                    L3.associative = 12;
                    L3.linesize = 64;
                    break;
                case 0xde:
                    L3.size = 8192;
                    L3.associative = 12;
                    L3.linesize = 64;
                    break;
                case 0xe2:
                    L3.size = 2048;
                    L3.associative = 16;
                    L3.linesize = 64;
                    break;
                case 0xe3:
                    L3.size = 4096;
                    L3.associative = 16;
                    L3.linesize = 64;
                    break;
                case 0xe4:
                    L3.size = 8192;
                    L3.associative = 16;
                    L3.linesize = 64;
                    break;
            }
        }
    }

    if (get_vendor() == VENDOR_INTEL) {
        if (LD1.size <= 0 || LC1.size <= 0) {
            // If we didn't detect L1 correctly before,
            int count;
            for (count = 0; count < 4; count++) {
                cpuid_count(4, count, &eax, &ebx, &ecx, &edx);
                switch (eax & 0x1f) {
                    case 0:
                        continue;
                    case 1:
                    case 3:
                    {
                        switch ((eax >> 5) & 0x07)
                        {
                            case 1:
                            {
                                // fprintf(stderr, "L1 data cache...\n");
                                int sets = ecx + 1;
                                int lines = (ebx & 0x0fff) + 1;
                                ebx >>= 12;
                                int part = (ebx & 0x03ff) + 1;
                                ebx >>= 10;
                                int assoc = (ebx & 0x03ff) + 1;
                                LD1.size = (assoc*part*lines*sets) / 1024;
                                LD1.associative = assoc;
                                LD1.linesize = lines;
                                break;
                            }
                            default:
                                break;
                        }
                        break;
                    }
                    case 2:
                    {
                        switch ((eax >> 5) & 0x07)
                        {
                            case 1:
                            {
                                // fprintf(stderr, "L1 instruction cache...\n");
                                int sets = ecx + 1;
                                int lines = (ebx & 0x0fff) + 1;
                                ebx >>= 12;
                                int part = (ebx & 0x03ff) + 1;
                                ebx >>= 10;
                                int assoc = (ebx & 0x03ff) + 1;
                                LC1.size = (assoc*part*lines*sets) / 1024;
                                LC1.associative = assoc;
                                LC1.linesize = lines;
                                break;
                            }
                            default:
                                break;
                        }
                        break;

                    }
                    default:
                        break;
                }
            }
        }
        cpuid(0x80000000, &cpuid_level, &ebx, &ecx, &edx);
        if (cpuid_level >= 0x80000006) {
            if (L2.size <= 0) {
                // If we didn't detect L2 correctly before,
                cpuid(0x80000006, &eax, &ebx, &ecx, &edx);

                L2.size = BITMASK(ecx, 16, 0xffff);
                L2.associative = BITMASK(ecx, 12, 0x0f);

                switch (L2.associative) {
                    case 0x06:
                        L2.associative = 8;
                        break;
                    case 0x08:
                        L2.associative = 16;
                        break;
                }

                L2.linesize = BITMASK(ecx, 0, 0xff);
            }
        }
    }

    if ((get_vendor() == VENDOR_AMD) ||
        (get_vendor() == VENDOR_HYGON) ||
        (get_vendor() == VENDOR_CENTAUR) ||
        (get_vendor() == VENDOR_ZHAOXIN)) {
        cpuid(0x80000005, &eax, &ebx, &ecx, &edx);

        LDTB.size = 4096;
        LDTB.associative = BITMASK(eax, 24, 0xff);
        if (LDTB.associative == 0xff) LDTB.associative = 0;
        LDTB.linesize = BITMASK(eax, 16, 0xff);

        LITB.size = 4096;
        LITB.associative = BITMASK(eax, 8, 0xff);
        if (LITB.associative == 0xff) LITB.associative = 0;
        LITB.linesize = BITMASK(eax, 0, 0xff);

        DTB.size = 4;
        DTB.associative = BITMASK(ebx, 24, 0xff);
        if (DTB.associative == 0xff) DTB.associative = 0;
        DTB.linesize = BITMASK(ebx, 16, 0xff);

        ITB.size = 4;
        ITB.associative = BITMASK(ebx, 8, 0xff);
        if (ITB.associative == 0xff) ITB.associative = 0;
        ITB.linesize = BITMASK(ebx, 0, 0xff);

        LD1.size = BITMASK(ecx, 24, 0xff);
        LD1.associative = BITMASK(ecx, 16, 0xff);
        if (LD1.associative == 0xff) LD1.associative = 0;
        LD1.linesize = BITMASK(ecx, 0, 0xff);

        LC1.size = BITMASK(ecx, 24, 0xff);
        LC1.associative = BITMASK(ecx, 16, 0xff);
        if (LC1.associative == 0xff) LC1.associative = 0;
        LC1.linesize = BITMASK(ecx, 0, 0xff);

        cpuid(0x80000006, &eax, &ebx, &ecx, &edx);

        L2LDTB.size = 4096;
        L2LDTB.associative = BITMASK(eax, 24, 0xff);
        if (L2LDTB.associative == 0xff) L2LDTB.associative = 0;
        L2LDTB.linesize = BITMASK(eax, 16, 0xff);

        L2LITB.size = 4096;
        L2LITB.associative = BITMASK(eax, 8, 0xff);
        if (L2LITB.associative == 0xff) L2LITB.associative = 0;
        L2LITB.linesize = BITMASK(eax, 0, 0xff);

        L2DTB.size = 4;
        L2DTB.associative = BITMASK(ebx, 24, 0xff);
        if (L2DTB.associative == 0xff) L2DTB.associative = 0;
        L2DTB.linesize = BITMASK(ebx, 16, 0xff);

        L2ITB.size = 4;
        L2ITB.associative = BITMASK(ebx, 8, 0xff);
        if (L2ITB.associative == 0xff) L2ITB.associative = 0;
        L2ITB.linesize = BITMASK(ebx, 0, 0xff);

        if (L2.size <= 0) {
            // If we didn't detect L2 correctly before,
            L2.size = BITMASK(ecx, 16, 0xffff);
            L2.associative = BITMASK(ecx, 12, 0xf);
            switch (L2.associative) {
                case 0x06:
                    L2.associative = 8;
                    break;
                case 0x08:
                    L2.associative = 16;
                    break;
            }

            if (L2.associative == 0xff) L2.associative = 0;
            L2.linesize = BITMASK(ecx, 0, 0xff);
        }

        L3.size = BITMASK(edx, 18, 0x3fff) * 512;
        L3.associative = BITMASK(edx, 12, 0xf);
        if (L3.associative == 0xff) L2.associative = 0;
        L3.linesize = BITMASK(edx, 0, 0xff);
    }

    switch (type) {
        case CACHE_INFO_L1_I:
            *cacheinfo = LC1;
            break;
        case CACHE_INFO_L1_D:
            *cacheinfo = LD1;
            break;
        case CACHE_INFO_L2:
            *cacheinfo = L2;
            break;
        case CACHE_INFO_L3:
            *cacheinfo = L3;
            break;
        case CACHE_INFO_L1_DTB:
            *cacheinfo = DTB;
            break;
        case CACHE_INFO_L1_ITB:
            *cacheinfo = ITB;
            break;
        case CACHE_INFO_L1_LDTB:
            *cacheinfo = LDTB;
            break;
        case CACHE_INFO_L1_LITB:
            *cacheinfo = LITB;
            break;
        case CACHE_INFO_L2_DTB:
            *cacheinfo = L2DTB;
            break;
        case CACHE_INFO_L2_ITB:
            *cacheinfo = L2ITB;
            break;
        case CACHE_INFO_L2_LDTB:
            *cacheinfo = L2LDTB;
            break;
        case CACHE_INFO_L2_LITB:
            *cacheinfo = L2LITB;
            break;
    }
    return 0;
}

int get_cpu_type(void)
{
    int family, exfamily, model, vendor, exmodel, stepping;

    if (!have_cpuid()) return CPUTYPE_80386;

    family = get_cpu_info(GET_FAMILY);
    exfamily = get_cpu_info(GET_EXFAMILY);
    model = get_cpu_info(GET_MODEL);
    exmodel = get_cpu_info(GET_EXMODEL);
    stepping = get_cpu_info(GET_STEPPING);

    vendor = get_vendor();

    if (vendor == VENDOR_INTEL) {
        switch (family) {
            case 0x4:
                return CPUTYPE_80486;
            case 0x5:
                return CPUTYPE_PENTIUM;
            case 0x6:
                switch (exmodel) {
                    case 0:
                        switch (model) {
                            case  1:
                            case  3:
                            case  5:
                            case  6:
#if defined(__x86_64__) || defined(__amd64__)
                                return CPUTYPE_CORE2;
#else
                                return CPUTYPE_PENTIUM2;
#endif
                            case  7:
                            case  8:
                            case 10:
                            case 11:
                                return CPUTYPE_PENTIUM3;
                            case  9:
                            case 13:
                            case 14:
                                return CPUTYPE_PENTIUMM;
                            case 15:
                                return CPUTYPE_CORE2;
                        }
                        break;
                    case 1:  // family 6 exmodel 1
                        switch (model) {
                            case  6:
                                return CPUTYPE_CORE2;
                            case  7:
                                return CPUTYPE_PENRYN;
                            case 10:
                            case 11:
                            case 14:
                            case 15:
                                return CPUTYPE_NEHALEM;
                            case 12:
                                return CPUTYPE_ATOM;
                            case 13:
                                return CPUTYPE_DUNNINGTON;
                        }
                        break;
                    case  2: // family 6 exmodel 2
                        switch (model) {
                            case 5:
                                // Intel Core (Clarkdale) / Core (Arrandale)
                                // Pentium (Clarkdale) / Pentium Mobile (Arrandale)
                                // Xeon (Clarkdale), 32nm
                                return CPUTYPE_NEHALEM;
                            case 10:
                                // Intel Core i5-2000 /i7-2000 (Sandy Bridge)
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM; //OS doesn't support AVX
                            case 12:
                                // Xeon Processor 5600 (Westmere-EP)
                                return CPUTYPE_NEHALEM;
                            case 13:
                                // Intel Core i7-3000 / Xeon E5 (Sandy Bridge)
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 14:
                                // Xeon E7540
                            case 15:
                                // Xeon Processor E7 (Westmere-EX)
                                return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 3: // family 6 exmodel 3
                        switch (model) {
                            case  7:
                                // Bay Trail
                                return CPUTYPE_ATOM;
                            case 10:
                            case 14:
                                // Ivy Bridge
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 12:
                            case 15:
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 13:
                                // Broadwell
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 4: // family 6 exmodel 4
                        switch (model) {
                            case 5:
                            case 6:
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 7:
                            case 15:
                                // Broadwell
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 14:
                                // Skylake
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 12:
                                // Braswell
                            case 13:
                                // Avoton
                                return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 5:  // family 6 exmodel 5
                        switch (model) {
                            case 6:
                                // Broadwell
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 5:
                                // Skylake X
                                if (support_avx512_bf16())
                                    return CPUTYPE_COOPERLAKE;
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 14:
                                // Skylake
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 7:
                                // Xeon Phi Knights Landing
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 12:
                                // Apollo Lake
                            case 15:
                                // Denverton
                                return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 6:  // family 6 exmodel 6
                        switch (model) {
                            case 6: // Cannon Lake
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;

                            case 10: // Ice Lake SP
                                if (support_avx512_bf16())
                                    return CPUTYPE_COOPERLAKE;
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 7: // family 6 exmodel 7
                        switch (model) {
                            case 10: // Goldmont Plus
                                return CPUTYPE_NEHALEM;
                            case 14: // Ice Lake
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 8:
                        switch (model) {
                            case 12: // Tiger Lake
                            case 13: // Tiger Lake (11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz)
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 14: // Kaby Lake and refreshes
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 15: // Sapphire Rapids
                                if (support_avx512_bf16())
                                    return CPUTYPE_COOPERLAKE;
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 9:
                        switch (model) {
                            case 7: // Alder Lake desktop
                            case 10: // Alder Lake mobile
                                if (support_avx512_bf16())
                                    return CPUTYPE_COOPERLAKE;
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 13: // Ice Lake NNPI
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 14: // Kaby Lake and refreshes
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 10: // family 6 exmodel 10
                        switch (model) {
                            case 5: // Comet Lake H and S
                            case 6: // Comet Lake U
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                            case 7: // Rocket Lake
                                if (support_avx512())
                                    return CPUTYPE_SKYLAKEX;
                                if (support_avx2())
                                    return CPUTYPE_HASWELL;
                                if (support_avx())
                                    return CPUTYPE_SANDYBRIDGE;
                                else
                                    return CPUTYPE_NEHALEM;
                        }
                        break;
                }
                break;
            case 0x7:
                return CPUTYPE_ITANIUM;
            case 0xf:
                switch (exfamily) {
                    case 0:
                        return CPUTYPE_PENTIUM4;
                    case 1:
                        return CPUTYPE_ITANIUM;
                }
                break;
        }
        return CPUTYPE_INTEL_UNKNOWN;
    }

    if (vendor == VENDOR_AMD) {
        switch (family) {
            case 0x4:
                return CPUTYPE_AMD5X86;
            case 0x5:
                return CPUTYPE_AMDK6;
            case 0x6:
#if defined(__x86_64__) || defined(__amd64__)
                return CPUTYPE_BARCELONA;
#else
                return CPUTYPE_ATHLON;
#endif
            case 0xf:
                switch (exfamily) {
                    case  0:
                    case  2:
                        return CPUTYPE_OPTERON;
                    case  1:
                    case  3:
                        // case  7:
                        // case 10:
                        return CPUTYPE_BARCELONA;
                    case  5:
                    case  7:
                        return CPUTYPE_BOBCAT;
                    case  6:
                        switch (model) {
                            case 1:
                                // AMD Bulldozer Opteron 6200 / Opteron 4200 / AMD FX-Series
                                if (support_avx())
                                    return CPUTYPE_BULLDOZER;
                                else
                                    return CPUTYPE_BARCELONA; //OS don't support AVX.
                            case 2: // AMD Piledriver
                            case 3: // AMD Richland
                                if (support_avx())
                                    return CPUTYPE_PILEDRIVER;
                                else
                                    return CPUTYPE_BARCELONA; //OS don't support AVX.
                            case 5: // New EXCAVATOR CPUS
                                if (support_avx())
                                    return CPUTYPE_EXCAVATOR;
                                else
                                    return CPUTYPE_BARCELONA; //OS don't support AVX.
                            case 0:
                            case 8:
                                switch (exmodel) {
                                    case 1: // AMD Trinity
                                        if (support_avx())
                                            return CPUTYPE_PILEDRIVER;
                                        else
                                            return CPUTYPE_BARCELONA; // OS don't support AVX.
                                    case 3:
                                        if (support_avx())
                                            return CPUTYPE_STEAMROLLER;
                                        else
                                            return CPUTYPE_BARCELONA; // OS don't support AVX.

                                    case 6:
                                        if (support_avx())
                                            return CPUTYPE_EXCAVATOR;
                                        else
                                            return CPUTYPE_BARCELONA; // OS don't support AVX.
                                }
                                break;
                        }
                        break;
                    case 8:
                        switch (model) {
                            case 1:
                                // AMD Ryzen
                            case 8:
                                // AMD Ryzen2
                            default:
                                // Matisse/Renoir and other recent Ryzen2
                                if (support_avx())
#ifndef NO_AVX2
                                    return CPUTYPE_ZEN;
#else
                                    return CPUTYPE_SANDYBRIDGE; // Zen is closer in architecture to Sandy Bridge than to Excavator
#endif
                                else
                                    return CPUTYPE_BARCELONA;
                        }
                        break;
                    case 10: // Zen3
                        if (support_avx())
#ifndef NO_AVX2
                            return CPUTYPE_ZEN;
#else
                            return CPUTYPE_SANDYBRIDGE; // Zen is closer in architecture to Sandy Bridge than to Excavator
#endif
                        else
                            return CPUTYPE_BARCELONA;
                        break;
                }
                break;
        }
        return CPUTYPE_AMD_UNKNOWN;
    }

    if (vendor == VENDOR_HYGON) {
        switch (family) {
            case 0xf:
                switch (exfamily) {
                    case 9:
                        // Hygon Dhyana
                        if (support_avx())
#ifndef NO_AVX2
                            return CPUTYPE_ZEN;
#else
                            return CPUTYPE_SANDYBRIDGE; // closer in architecture to Sandy Bridge than to Excavator
#endif
                        else
                            return CPUTYPE_BARCELONA;
                }
                break;
        }
        return CPUTYPE_HYGON_UNKNOWN;
    }

    if (vendor == VENDOR_CYRIX) {
        switch (family) {
            case 0x4:
                return CPUTYPE_CYRIX5X86;
            case 0x5:
                return CPUTYPE_CYRIXM1;
            case 0x6:
                return CPUTYPE_CYRIXM2;
        }
        return CPUTYPE_CYRIX_UNKNOWN;
    }

    if (vendor == VENDOR_NEXGEN) {
        switch (family) {
            case 0x5:
                return CPUTYPE_NEXGENNX586;
        }
        return CPUTYPE_NEXGEN_UNKNOWN;
    }

    if (vendor == VENDOR_CENTAUR) {
        switch (family) {
            case 0x5:
                return CPUTYPE_CENTAURC6;
                break;
            case 0x6:
                if (model == 0xf && stepping < 0xe)
                    return CPUTYPE_NANO;
                else
                    return CPUTYPE_NEHALEM;
                break;
            case 0x7:
                switch (exmodel) {
                    case 5:
                        if (support_avx2())
                            return CPUTYPE_ZEN;
                        else
                            return CPUTYPE_DUNNINGTON;
                    default:
                        return CPUTYPE_NEHALEM;
                }
                break;
            default:
                if (family >= 0x8)
                    return CPUTYPE_NEHALEM;
                else
                    return CPUTYPE_VIAC3;
        }
    }

    if (vendor == VENDOR_ZHAOXIN) {
        switch (family) {
            case 0x7:
                switch (exmodel) {
                    case 5:
                        if (support_avx2())
                            return CPUTYPE_ZEN;
                        else
                            return CPUTYPE_DUNNINGTON;
                    default:
                        return CPUTYPE_NEHALEM;
                }
            default:
                return CPUTYPE_NEHALEM;
        }
    }

    if (vendor == VENDOR_RISE) {
        switch (family) {
            case 0x5:
                return CPUTYPE_RISEMP6;
        }
        return CPUTYPE_RISE_UNKNOWN;
    }

    if (vendor == VENDOR_SIS) {
        switch (family) {
            case 0x5:
                return CPUTYPE_SYS55X;
        }
        return CPUTYPE_SIS_UNKNOWN;
    }

    if (vendor == VENDOR_TRANSMETA) {
        switch (family) {
            case 0x5:
                return CPUTYPE_CRUSOETM3X;
        }
        return CPUTYPE_TRANSMETA_UNKNOWN;
    }

    if (vendor == VENDOR_NSC) {
        switch (family) {
            case 0x5:
                return CPUTYPE_NSGEODE;
        }
        return CPUTYPE_NSC_UNKNOWN;
    }

    return CPUTYPE_UNKNOWN;
}

int get_core_type(void)
{
    int family, exfamily, model, exmodel, vendor, stepping;

    if (!have_cpuid()) return CORE_80486;

    family = get_cpu_info(GET_FAMILY);
    exfamily = get_cpu_info(GET_EXFAMILY);
    model = get_cpu_info(GET_MODEL);
    exmodel = get_cpu_info(GET_EXMODEL);
    stepping = get_cpu_info(GET_STEPPING);

    vendor = get_vendor();

    if (vendor == VENDOR_INTEL) {
        switch (family) {
            case  4:
                return CORE_80486;
            case  5:
                return CORE_P5;
            case  6:
                switch (exmodel) {
                    case  0:
                        switch (model) {
                            case  0:
                            case  1:
                            case  2:
                            case  3:
                            case  4:
                            case  5:
                            case  6:
#if defined(__x86_64__) || defined(__amd64__)
                                return CORE_CORE2;
#else
                                return CORE_P6;
#endif
                            case  7:
                                return CORE_KATMAI;
                            case  8:
                            case 10:
                            case 11:
                                return CORE_COPPERMINE;
                            case  9:
                            case 13:
                            case 14:
                                return CORE_BANIAS;
                            case 15:
                                return CORE_CORE2;
                        }
                        break;
                    case  1:
                        switch (model) {
                            case  6:
                                return CORE_CORE2;
                            case  7:
                                return CORE_PENRYN;
                            case 10:
                            case 11:
                            case 14:
                            case 15:
                                return CORE_NEHALEM;
                            case 12:
                                return CORE_ATOM;
                            case 13:
                                return CORE_DUNNINGTON;
                        }
                        break;
                    case  2:
                        switch (model) {
                            case 5:
                                // Intel Core (Clarkdale) / Core (Arrandale)
                                // Pentium (Clarkdale) / Pentium Mobile (Arrandale)
                                // Xeon (Clarkdale), 32nm
                                return CORE_NEHALEM;
                            case 10:
                                // Intel Core i5-2000 /i7-2000 (Sandy Bridge)
                                if (support_avx())
                                    return CORE_SANDYBRIDGE;
                                else
                                    return CORE_NEHALEM; // OS doesn't support AVX
                            case 12:
                                // Xeon Processor 5600 (Westmere-EP)
                                return CORE_NEHALEM;
                            case 13:
                                // Intel Core i7-3000 / Xeon E5 (Sandy Bridge)
                                if (support_avx())
                                    return CORE_SANDYBRIDGE;
                                else
                                    return CORE_NEHALEM; // OS doesn't support AVX
                            case 14:
                                // Xeon E7540
                            case 15:
                                // Xeon Processor E7 (Westmere-EX)
                                return CORE_NEHALEM;
                        }
                        break;
                    case 3:
                        switch (model) {
                            case 7:
                                return CORE_ATOM;
                            case 10:
                            case 14:
                                if (support_avx())
                                    return CORE_SANDYBRIDGE;
                                else
                                    return CORE_NEHALEM; // OS doesn't support AVX
                            case 12:
                            case 15:
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 13:
                                // broadwell
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                        }
                        break;
                    case 4:
                        switch (model) {
                            case 5:
                            case 6:
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 7:
                            case 15:
                                // broadwell
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 14:
                                // Skylake
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 12:
                                // Braswell
                            case 13:
                                // Avoton
                                return CORE_NEHALEM;
                        }
                        break;
                    case 5:
                        switch (model) {
                            case 6:
                                // broadwell
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 5:
                                // Skylake X
#ifndef NO_AVX512
                                if (support_avx512_bf16())
                                    return CORE_COOPERLAKE;
                                else
                                    return CORE_SKYLAKEX;
#else
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
#endif
                            case 14:
                                // Skylake
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 7:
                                // Phi Knights Landing
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 12:
                                // Apollo Lake
                                return CORE_NEHALEM;
                        }
                        break;
                    case 6:
                        if (model == 6) {
#ifndef NO_AVX512

                            return CORE_SKYLAKEX;
#else
                            if (support_avx())
#ifndef NO_AVX2
                                return CORE_HASWELL;
#else
                                return CORE_SANDYBRIDGE;
#endif
                            else
                                return CORE_NEHALEM;
#endif
                        }

                        if (model == 10 || model == 12) {
#ifndef NO_AVX512
                            if (support_avx512_bf16())
                                return CORE_COOPERLAKE;
                            else
                                return CORE_SKYLAKEX;
#else
                            if (support_avx())
#ifndef NO_AVX2
                                return CORE_HASWELL;
#else
                                return CORE_SANDYBRIDGE;
#endif
                            else
                                return CORE_NEHALEM;
#endif
                        }
                        break;
                    case 7:
                        if (model == 10)
                            return CORE_NEHALEM;
                        if (model == 13 || model == 14) { // Ice Lake
#ifndef NO_AVX512
                            return CORE_SKYLAKEX;
#else
                            if (support_avx())
#ifndef NO_AVX2
                                return CORE_HASWELL;
#else
                                return CORE_SANDYBRIDGE;
#endif
                            else
                                return CORE_NEHALEM;
#endif
                        }
                        break;
                    case 8:
                        if (model == 12 || model == 13) { // Tiger Lake
                            if (support_avx512())
                                return CORE_SKYLAKEX;
                            if (support_avx2())
                                return CORE_HASWELL;
                            if (support_avx())
                                return CORE_SANDYBRIDGE;
                            else
                                return CORE_NEHALEM;
                        } else if (model == 14) { // Kaby Lake mobile
                            if (support_avx())
#ifndef NO_AVX2
                                return CORE_HASWELL;
#else
                                return CORE_SANDYBRIDGE;
#endif
                            else
                                return CORE_NEHALEM;
                        } else if (model == 15) { // Sapphire Rapids
                            if (support_avx512_bf16())
                                return CPUTYPE_COOPERLAKE;
                            if (support_avx512())
                                return CPUTYPE_SKYLAKEX;
                            if (support_avx2())
                                return CPUTYPE_HASWELL;
                            if (support_avx())
                                return CPUTYPE_SANDYBRIDGE;
                            else
                                return CPUTYPE_NEHALEM;
                        }
                        break;
                    case 9:
                        if (model == 7 || model == 10) { // Alder Lake
                            if (support_avx2())
                                return CORE_HASWELL;
                            if (support_avx())
                                return CORE_SANDYBRIDGE;
                            else
                                return CORE_NEHALEM;
                        }
                        else if (model == 13) { // Ice Lake NNPI
                            if (support_avx512())
                                return CORE_SKYLAKEX;
                            if (support_avx2())
                                return CORE_HASWELL;
                            if (support_avx())
                                return CORE_SANDYBRIDGE;
                            else
                                return CORE_NEHALEM;
                        }
                        else if (model == 14) { // Kaby Lake desktop
                            if (support_avx())
#ifndef NO_AVX2
                                return CORE_HASWELL;
#else
                                return CORE_SANDYBRIDGE;
#endif
                            else
                                return CORE_NEHALEM;
                        }
                        break;
                    case 10:
                        switch (model) {
                            case 5: // Comet Lake H and S
                            case 6: // Comet Lake U
                                if (support_avx())
#ifndef NO_AVX2
                                    return CORE_HASWELL;
#else
                                    return CORE_SANDYBRIDGE;
#endif
                                else
                                    return CORE_NEHALEM;
                            case 7: // Rocket Lake
#ifndef NO_AVX512
                                if (support_avx512())
                                    return CORE_SKYLAKEX;
#endif
#ifndef NO_AVX2
                                if (support_avx2())
                                    return CORE_HASWELL;
#endif
                                if (support_avx())
                                    return CORE_SANDYBRIDGE;
                                else
                                    return CORE_NEHALEM;
                        }
                        break;
                    case 15:
                        if (model <= 0x2)
                            return CORE_NORTHWOOD;
                        else
                            return CORE_PRESCOTT;
                        break;
                }
        }
    }

    if (vendor == VENDOR_AMD) {
        if (family <= 0x5) return CORE_80486;
#if defined(__x86_64__) || defined(__amd64__)
        if (family <= 0xe) return CORE_BARCELONA;
#else
        if (family <= 0xe) return CORE_ATHLON;
#endif
        if (family == 0xf) {
            if ((exfamily == 0) || (exfamily == 2)) return CORE_OPTERON;
            else if (exfamily == 5) return CORE_BOBCAT;
            else if (exfamily == 6) {
                switch (model) {
                    case 1:
                        // AMD Bulldozer Opteron 6200 / Opteron 4200 / AMD FX-Series
                        if (support_avx())
                            return CORE_BULLDOZER;
                        else
                            return CORE_BARCELONA; // OS don't support AVX.
                    case 2: // AMD Piledriver
                    case 3: // AMD Richland
                        if (support_avx())
                            return CORE_PILEDRIVER;
                        else
                            return CORE_BARCELONA; // OS don't support AVX.
                    case 5: // New EXCAVATOR
                        if (support_avx())
                            return CORE_EXCAVATOR;
                        else
                            return CORE_BARCELONA; // OS don't support AVX.
                    case 0:
                    case 8:
                        switch (exmodel) {
                            case 1: //AMD Trinity
                                if (support_avx())
                                    return CORE_PILEDRIVER;
                                else
                                    return CORE_BARCELONA; // OS don't support AVX.

                            case 3:
                                if (support_avx())
                                    return CORE_STEAMROLLER;
                                else
                                    return CORE_BARCELONA; // OS don't support AVX.

                            case 6:
                                if (support_avx())
                                    return CORE_EXCAVATOR;
                                else
                                    return CORE_BARCELONA; // OS don't support AVX.
                        }
                        break;
                }
            }
            else if (exfamily == 8 || exfamily == 10) {
                switch (model) {
                    case 1:
                        // AMD Ryzen
                    case 8:
                        // Ryzen 2
                    default:
                        // Matisse,Renoir Ryzen2 models
                        if (support_avx())
#ifndef NO_AVX2
                            return CORE_ZEN;
#else
                            return CORE_SANDYBRIDGE; // Zen is closer in architecture to Sandy Bridge than to Excavator
#endif
                        else
                            return CORE_BARCELONA;
                }
            }
            else {
                return CORE_BARCELONA;
            }
        }
    }

    if (vendor == VENDOR_HYGON) {
        if (family == 0xf) {
            if (exfamily == 9) {
                if (support_avx())
#ifndef NO_AVX2
                    return CORE_ZEN;
#else
                    return CORE_SANDYBRIDGE; // closer in architecture to Sandy Bridge than to Excavator
#endif
                else
                    return CORE_BARCELONA;
            }
            else {
                return CORE_BARCELONA;
            }
        }
    }

    if (vendor == VENDOR_CENTAUR) {
        switch (family) {
            case 0x6:
                if (model == 0xf && stepping < 0xe)
                    return CORE_NANO;
                else
                    return CORE_NEHALEM;
                break;
            case 0x7:
                switch (exmodel) {
                    case 5:
                        if (support_avx2())
                            return CORE_ZEN;
                        else
                            return CORE_DUNNINGTON;
                    default:
                        return CORE_NEHALEM;
                }
                break;
            default:
                if (family >= 0x8)
                    return CORE_NEHALEM;
                else
                    return CORE_VIAC3;
        }
    }

    if (vendor == VENDOR_ZHAOXIN) {
        switch (family) {
            case 0x7:
                switch (exmodel) {
                    case 5:
                        if (support_avx2())
                            return CORE_ZEN;
                        else
                            return CORE_DUNNINGTON;
                    default:
                        return CORE_NEHALEM;
                }
            default:
                return CORE_NEHALEM;
        }
    }

    return CORE_UNKNOWN;
}

static const char * cpuname[] = {
    "UNKNOWN",
    "INTEL_UNKNOWN",
    "UMC_UNKNOWN",
    "AMD_UNKNOWN",
    "CYRIX_UNKNOWN",
    "NEXGEN_UNKNOWN",
    "CENTAUR_UNKNOWN",
    "RISE_UNKNOWN",
    "SIS_UNKNOWN",
    "TRANSMETA_UNKNOWN",
    "NSC_UNKNOWN",
    "80386",
    "80486",
    "PENTIUM",
    "PENTIUM2",
    "PENTIUM3",
    "PENTIUMM",
    "PENTIUM4",
    "CORE2",
    "PENRYN",
    "DUNNINGTON",
    "NEHALEM",
    "ATOM",
    "ITANIUM",
    "ITANIUM2",
    "5X86",
    "K6",
    "ATHLON",
    "DURON",
    "OPTERON",
    "BARCELONA",
    "SHANGHAI",
    "ISTANBUL",
    "CYRIX5X86",
    "CYRIXM1",
    "CYRIXM2",
    "NEXGENNX586",
    "CENTAURC6",
    "RISEMP6",
    "SYS55X",
    "TM3X00",
    "NSGEODE",
    "VIAC3",
    "NANO",
    "SANDYBRIDGE",
    "BOBCAT",
    "BULLDOZER",
    "PILEDRIVER",
    "HASWELL",
    "STEAMROLLER",
    "EXCAVATOR",
    "ZEN",
    "SKYLAKEX",
    "DHYANA",
    "COOPERLAKE"
};

static const char * cpuname_lower[] = {
    "unknown",
    "intel_unknown",
    "umc_unknown",
    "amd_unknown",
    "cyrix_unknown",
    "nexgen_unknown",
    "centaur_unknown",
    "rise_unknown",
    "sis_unknown",
    "transmeta_unknown",
    "nsc_unknown",
    "80386",
    "80486",
    "pentium",
    "pentium2",
    "pentium3",
    "pentiumm",
    "pentium4",
    "core2",
    "penryn",
    "dunnington",
    "nehalem",
    "atom",
    "itanium",
    "itanium2",
    "5x86",
    "k6",
    "athlon",
    "duron",
    "opteron",
    "barcelona",
    "shanghai",
    "istanbul",
    "cyrix5x86",
    "cyrixm1",
    "cyrixm2",
    "nexgennx586",
    "centaurc6",
    "risemp6",
    "sys55x",
    "tms3x00",
    "nsgeode",
    "nano",
    "sandybridge",
    "bobcat",
    "bulldozer",
    "piledriver",
    "haswell",
    "steamroller",
    "excavator",
    "zen",
    "skylakex",
    "dhyana",
    "cooperlake"
};

static const char * corename[] = {
    "UNKNOWN",
    "80486",
    "P5",
    "P6",
    "KATMAI",
    "COPPERMINE",
    "NORTHWOOD",
    "PRESCOTT",
    "BANIAS",
    "ATHLON",
    "OPTERON",
    "BARCELONA",
    "VIAC3",
    "YONAH",
    "CORE2",
    "PENRYN",
    "DUNNINGTON",
    "NEHALEM",
    "ATOM",
    "NANO",
    "SANDYBRIDGE",
    "BOBCAT",
    "BULLDOZER",
    "PILEDRIVER",
    "HASWELL",
    "STEAMROLLER",
    "EXCAVATOR",
    "ZEN",
    "SKYLAKEX",
    "DHYANA",
    "COOPERLAKE"
};

static const char * corename_lower[] = {
    "unknown",
    "80486",
    "p5",
    "p6",
    "katmai",
    "coppermine",
    "northwood",
    "prescott",
    "banias",
    "athlon",
    "opteron",
    "barcelona",
    "viac3",
    "yonah",
    "core2",
    "penryn",
    "dunnington",
    "nehalem",
    "atom",
    "nano",
    "sandybridge",
    "bobcat",
    "bulldozer",
    "piledriver",
    "haswell",
    "steamroller",
    "excavator",
    "zen",
    "skylakex",
    "dhyana",
    "dhyana",
    "cooperlake"
};

static const char * cpu_features[] = {
    "x87 FPU On Chip",
    "Virtual-8086 Mode Enhancement",
    "Debugging Extensions",
    "Page Size Extensions",
    "Time Stamp Counter",
    "RDMSR and WRMSR Support",
    "Physical Address Extensions",
    "Machine Check Exception",
    "CMPXCHG8B Instruction",
    "APIC On Chip",
    "Unknown1",
    "SYSENTER and SYSEXIT",
    "Memory Type Range Registers",
    "PTE Global Bit",
    "Machine Check Architecture",
    "Conditional Move/Compare Instruction",
    "Page Attribute Table",
    "36-bit Page Size Extension",
    "Processor Serial Number",
    "CFLUSH Extension",
    "Unknown2",
    "Debug Store",
    "Thermal Monitor and Clock Ctrl",
    "MMX Technology",
    "FXSAVE/FXRSTOR",
    "SSE Extensions",
    "SSE2 Extensions",
    "Self Snoop",
    "Multithreading Technology",
    "Thermal Monitor",
    "Unknown4",
    "Pend. Brk. EN."
};

const char * get_cpuname(void)
{
    return cpuname[get_cpu_type()];
}

const char * get_lower_cpuname(void)
{
    return cpuname_lower[get_cpu_type()];
}

const char * get_corename(void)
{
    return corename[get_core_type()];
}

void get_libname(void)
{
    printf("%s", corename_lower[get_core_type()]);
}

void get_architecture(void)
{
#ifndef __64BIT__
    printf("X86");
#else
    printf("X86_64");
#endif
}

void get_subarchitecture(void)
{
    printf("%s", get_cpuname());
}

void get_subdirname(void)
{
#ifndef __64BIT__
    printf("x86");
#else
    printf("x86_64");
#endif
}

void get_cpu_config(void)
{
    cache_info_t info;
    int features, coretype;

    printf("#define %s\n", get_cpuname());

    if (get_core_type() != CORE_P5) {
        get_cacheinfo(CACHE_INFO_L1_I, &info);
        if (info.size > 0) {
            printf("#define L1_CODE_SIZE %d\n", info.size * 1024);
            printf("#define L1_CODE_ASSOCIATIVE %d\n", info.associative);
            printf("#define L1_CODE_LINESIZE %d\n", info.linesize);
        }

        get_cacheinfo(CACHE_INFO_L1_D, &info);
        if (info.size > 0) {
            printf("#define L1_DATA_SIZE %d\n", info.size * 1024);
            printf("#define L1_DATA_ASSOCIATIVE %d\n", info.associative);
            printf("#define L1_DATA_LINESIZE %d\n", info.linesize);
        }

        get_cacheinfo(CACHE_INFO_L2, &info);
        if (info.size > 0) {
            printf("#define L2_SIZE %d\n", info.size * 1024);
            printf("#define L2_ASSOCIATIVE %d\n", info.associative);
            printf("#define L2_LINESIZE %d\n", info.linesize);
        }
        else {
            // fall back for some virtual machines.
            printf("#define L2_SIZE 1048576\n");
            printf("#define L2_ASSOCIATIVE 6\n");
            printf("#define L2_LINESIZE 64\n");
        }

        get_cacheinfo(CACHE_INFO_L3, &info);
        if (info.size > 0) {
            printf("#define L3_SIZE %d\n", info.size * 1024);
            printf("#define L3_ASSOCIATIVE %d\n", info.associative);
            printf("#define L3_LINESIZE %d\n", info.linesize);
        }

        get_cacheinfo(CACHE_INFO_L1_ITB, &info);
        if (info.size > 0) {
            printf("#define ITB_SIZE %d\n", info.size * 1024);
            printf("#define ITB_ASSOCIATIVE %d\n", info.associative);
            printf("#define ITB_ENTRIES %d\n", info.linesize);
        }

        get_cacheinfo(CACHE_INFO_L1_DTB, &info);
        if (info.size > 0) {
            printf("#define DTB_SIZE %d\n", info.size * 1024);
            printf("#define DTB_ASSOCIATIVE %d\n", info.associative);
            printf("#define DTB_DEFAULT_ENTRIES %d\n", info.linesize);
        }
        else {
            // fall back for some virtual machines.
            printf("#define DTB_DEFAULT_ENTRIES 32\n");
        }

        features = get_cpu_info(GET_FEATURE);

        if (features & HAVE_CMOV)       printf("#define HAVE_CMOV\n");
        if (features & HAVE_MMX)        printf("#define HAVE_MMX\n");
        if (features & HAVE_SSE)        printf("#define HAVE_SSE\n");
        if (features & HAVE_SSE2)       printf("#define HAVE_SSE2\n");
        if (features & HAVE_SSE3)       printf("#define HAVE_SSE3\n");
        if (features & HAVE_SSSE3)      printf("#define HAVE_SSSE3\n");
        if (features & HAVE_SSE4_1)     printf("#define HAVE_SSE4_1\n");
        if (features & HAVE_SSE4_2)     printf("#define HAVE_SSE4_2\n");
        if (features & HAVE_SSE4A)      printf("#define HAVE_SSE4A\n");
        if (features & HAVE_SSE5)       printf("#define HAVE_SSSE5\n");
        if (features & HAVE_AVX)        printf("#define HAVE_AVX\n");
        if (features & HAVE_AVX2)       printf("#define HAVE_AVX2\n");
        if (features & HAVE_AVX512VL)   printf("#define HAVE_AVX512VL\n");
        if (features & HAVE_AVX512BF16) printf("#define HAVE_AVX512BF16\n");
        if (features & HAVE_AMXBF16)    printf("#define HAVE_AMXBF16\n");
        if (features & HAVE_3DNOWEX)    printf("#define HAVE_3DNOWEX\n");
        if (features & HAVE_3DNOW)      printf("#define HAVE_3DNOW\n");
        if (features & HAVE_FMA4)       printf("#define HAVE_FMA4\n");
        if (features & HAVE_FMA3)       printf("#define HAVE_FMA3\n");
        if (features & HAVE_CFLUSH)     printf("#define HAVE_CFLUSH\n");
        if (features & HAVE_HIT)        printf("#define HAVE_HIT 1\n");
        if (features & HAVE_MISALIGNSSE) printf("#define HAVE_MISALIGNSSE\n");
        if (features & HAVE_128BITFPU)  printf("#define HAVE_128BITFPU\n");
        if (features & HAVE_FASTMOVU)   printf("#define HAVE_FASTMOVU\n");

        printf("#define NUM_SHAREDCACHE %d\n", get_cpu_info(GET_NUMSHARE) + 1);
        printf("#define NUM_CORES %d\n", get_cpu_info(GET_NUMCORES) + 1);

        coretype = get_core_type();
        if (coretype > 0) printf("#define CORE_%s\n", get_corename());
    }
    else {
        printf("#define DTB_DEFAULT_ENTRIES 16\n");
        printf("#define L1_CODE_SIZE 8192\n");
        printf("#define L1_DATA_SIZE 8192\n");
        printf("#define L2_SIZE 0\n");
    }
}

/* This if for Makefile */
void get_sse(void)
{
    int features;
    features = get_cpu_info(GET_FEATURE);

    if (features & HAVE_MMX)        printf("HAVE_MMX=1\n");
    if (features & HAVE_SSE)        printf("HAVE_SSE=1\n");
    if (features & HAVE_SSE2)       printf("HAVE_SSE2=1\n");
    if (features & HAVE_SSE3)       printf("HAVE_SSE3=1\n");
    if (features & HAVE_SSSE3)      printf("HAVE_SSSE3=1\n");
    if (features & HAVE_SSE4_1)     printf("HAVE_SSE4_1=1\n");
    if (features & HAVE_SSE4_2)     printf("HAVE_SSE4_2=1\n");
    if (features & HAVE_SSE4A)      printf("HAVE_SSE4A=1\n");
    if (features & HAVE_SSE5)       printf("HAVE_SSSE5=1\n");
    if (features & HAVE_AVX)        printf("HAVE_AVX=1\n");
    if (features & HAVE_AVX2)       printf("HAVE_AVX2=1\n");
    if (features & HAVE_AVX512VL)   printf("HAVE_AVX512VL=1\n");
    if (features & HAVE_AVX512BF16) printf("HAVE_AVX512BF16=1\n");
    if (features & HAVE_AMXBF16)    printf("HAVE_AMXBF16=1\n");
    if (features & HAVE_3DNOWEX)    printf("HAVE_3DNOWEX=1\n");
    if (features & HAVE_3DNOW)      printf("HAVE_3DNOW=1\n");
    if (features & HAVE_FMA4)       printf("HAVE_FMA4=1\n");
    if (features & HAVE_FMA3)       printf("HAVE_FMA3=1\n");
}
